package br.ufrj.dcc.trabxml;

import java.io.IOException;
import java.util.TreeMap;

import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Node;

import br.ufrj.dcc.greco.communication.Connector;
import br.ufrj.dcc.trabxml.manager.XmlManager;
import br.ufrj.dcc.trabxml.util.XmlUtil;

public class MyConnector extends Connector {

    private static MyConnector instance = null;

    private final TreeMap<String, XmlReceiver> waiting = new TreeMap<String, XmlReceiver>();
    
    private final TreeMap<String, Thread> killers = new TreeMap<String, Thread>();

    public static void create(int port) throws IOException {
        instance = new MyConnector(port);
    }

    public static MyConnector getInstance() {
        return instance;
    }

    private MyConnector(int port) throws IOException {
        super(port);
    }

    public Thread send(String host, int port, String xml, XmlReceiver receiver)
            throws IOException{
        String id = getId(xml);
        if (XmlManager.isCatalogRequest(xml)) {
            id = "catalog"+XmlManager.getMessageReceiver(xml);
        }
        final String finalId = id;
        System.out.println("New Message => " + id);
        Thread killer = new Thread() {
            public void run() {
                try {
                    sleep(40000);
                } catch (InterruptedException e) {
                }
                System.out.println("Removing "+finalId);
                waiting.remove(finalId);
                killers.remove(finalId);
            }
        };
        killers.put(id, killer);
        waiting.put(id, receiver);
        killer.start();
        this.send(host, port, xml);
        return killer;
    }
    
    public void send(String host, int port, String xml) {
        try {
            System.out.println(XmlUtil.prettyPrint(xml));
            xml = XmlManager.transformToPublicXML(xml);
            System.out.println(XmlUtil.prettyPrint(xml));
            if(!XmlManager.validatePublicXML(xml)) {
                System.out.println("XML publico invalido! Esta mensagem nao deveria ser exibida.");
                return;
            }        
            super.send(host, port, xml);
        } catch (Exception e) {
            
        }
    }
    
    @Override
    public void receive(String xml) {
        System.out.println(XmlUtil.prettyPrint(xml));
        try {
            if (XmlManager.validatePublicXML(xml)) {
                xml = XmlManager.transformToInternalXML(xml);
                System.out.println(XmlUtil.prettyPrint(xml));
            } else {
                System.out.println("O xml recebido eh invalido!");
                return;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }        
        String id = getId(xml);
        if (XmlManager.isCatalog(xml)) {
            id = "catalog"+XmlManager.getCatalogOwner(xml);
        }
        if (XmlManager.isCatalogRequest(xml) || XmlManager.isLendOrReturnRequest(xml)) {
            id = null;
        }
        if (id == null) {
            processRequest(xml);
        }
        if (id != null) {
            Thread killer = killers.get(id);  
            XmlReceiver rcv = waiting.get(id);
            if (rcv != null) {
                rcv.receive(xml);
            }            
            if (killer != null)
                killer.interrupt();        
        }
    }
    
    private void processRequest(String xml) {
        try {
            RequestProcessor.processRequest(xml);
        } catch (Exception e) {
            e.printStackTrace();
        }        
    }

    @Override
    public void reportException(Exception exception) {
        exception.printStackTrace();
    }
    
    private String getId(String xml) {
        try {
            Node root = XmlUtil.getDocumentRoot(xml);
            XPath xpath = XPathFactory.newInstance().newXPath();
            String id = ((Node) xpath.evaluate("@id", root,
                    XPathConstants.NODE)).getNodeValue();
            return id;
        } catch (Exception e) {
            return null;
        }
    }

}
